#include "map.h"


/*
 * map_width and map_height is how many locations you want to blit, NOT pixels
 * surface_x and surfacy_y are the PIXELS on the surface where the map will blit to
 * 
 * Optimizing this is at the absolute bottom of my list of priorities
 */
int mW_map_blit (mW_map * map, BITMAP * surface, int map_width, int map_height, int surface_x, int surface_y)
{
	
	int position_x;
	int position_y;
	
	int start_x;
	int end_x;
	
	int start_y;
	int end_y;
	
	int map_x;
	int map_y;
	
	int pixel_x;
	int pixel_y;
	
	BITMAP * tile_to_blit;
	BITMAP * map_buffer_page;
	
	map_buffer_page = create_bitmap(map_width * mW_TILE_SIZE, map_height * mW_TILE_SIZE);
	
	
	if (map->offset_x > 0)
	{
		start_x = -1 - (map->offset_x / mW_TILE_SIZE);
		end_x   = map_width - map->offset_x / mW_TILE_SIZE;
	}
	else
	{
		start_x = map->offset_x / mW_TILE_SIZE;
		end_x   = map_width + 1 - (map->offset_x / mW_TILE_SIZE);
	}
	
	if (map->offset_y > 0)
	{
		start_y = -1 - (map->offset_y / mW_TILE_SIZE);
		end_y   = map_height - (map->offset_y / mW_TILE_SIZE);
	}
	else
	{
		start_y = (map->offset_y / mW_TILE_SIZE);
		end_y   = map_height + 1 - (map->offset_y / mW_TILE_SIZE);
	}
	
	
	acquire_bitmap(map_buffer_page);
	
	for (position_x = start_x; position_x < end_x; position_x++)
	{
		
		for (position_y = start_y; position_y < end_y; position_y++)
		{
			map_x = position_x + map->left;
			map_y = position_y + map->top;
			
			
			pixel_x = (position_x * mW_TILE_SIZE) + map->offset_x;
			pixel_y = (position_y * mW_TILE_SIZE) + map->offset_y;
			
			if ((map_x >= 0) && (map_x < map->width) && (map_y >= 0) && (map_y < map->height))
				tile_to_blit = mW_tiles[map->locations[map_x][map_y]->tile_id];
			else
				tile_to_blit = mW_tiles_no_tile;

			mW_blit(tile_to_blit,
			        map_buffer_page,
			        pixel_x,
			        pixel_y);
			        
			if ((map->flags & 1) > 0)
			{
				if ((map_x >= 0) && (map_x < map->width) && (map_y >= 0) && (map_y < map->height))
				{
					if ((map->locations[map_x][map_y]->flags & 1) > 0)
						mW_blit(mW_tile_map_passable, map_buffer_page, pixel_x, pixel_y);
					else
						mW_blit(mW_tile_map_nonpassable, map_buffer_page, pixel_x, pixel_y);
				}
			}
			        
		}
	
	}
	
	release_bitmap(map_buffer_page);
	
	acquire_bitmap(surface);
	mW_blit(map_buffer_page, surface, 0, 0);
	release_bitmap(surface);
	
	destroy_bitmap(map_buffer_page);
	
	return 1;
	
}



int mW_map_center (mW_map * map, int x, int y)
{
	
	mW_map_scroll_to(map, x - (map->display_width / 2), y - (map->display_height / 2));
	
	return 1;

}



/*
 * Initializes mW_map_struct
 *   allocates memory, etc
 * Returns 1 on success, 0 on failure
 */
mW_map * mW_map_create (int width, int height)
{
	
	int width_i = 0;
	int height_i = 0;
	
	int memory_break = 0;
	unsigned short free_width_i;
	unsigned short free_height_i;
	
	mW_map * map;
	
	map = (mW_map *) malloc(sizeof(mW_map));
	if (map == NULL)
		mW_error("Error allocating memory for map");
	
	map->offset_x = 0;
	map->offset_y = 0;

	map->display_width = 0;
	map->display_height = 0;
	
	map->top = 0;
	map->left = 0;
	
	map->width = width;
	map->height = height;
	
	map->flags = 0;
	
	//allocate memory for width locations pointers in one malloc
	map->locations = malloc(sizeof(mW_map_location **) * width);
	if (map->locations == NULL)
		return 0;


	// allocate memory for width locations
	for (width_i = 0; width_i < width; width_i++)
	{
		
		mW_map_location ** test;
		test = (mW_map_location **) malloc(sizeof(mW_map_location *) * height);
		
		map->locations[width_i] = (mW_map_location **) malloc(sizeof(mW_map_location *) * height);
		if (map->locations[width_i] == NULL)
		{
			memory_break = 1;
			break;
		}
		
		// allocate memory for locations
		for (height_i = 0; height_i < height; height_i++)
		{
			
			map->locations[width_i][height_i] = (mW_map_location *) malloc(sizeof(mW_map_location));
			if (map->locations[width_i][height_i] == NULL)
			{
				memory_break = 1;
				break;
			}
			// and set it all to zero
			memset(map->locations[width_i][height_i], 0, sizeof(mW_map_location));
			map->locations[width_i][height_i]->next_location = NULL;
			
		}
	}
	
	
	// error allocating memory
	if (memory_break == 1)
	{
		for (free_width_i = 0; free_width_i < width_i; free_width_i++)
		{
			for (free_height_i = 0; free_height_i < height_i; free_height_i++)
			{
				if (map->locations[free_width_i][free_height_i] != NULL)
					free(map->locations[free_width_i][free_height_i]);
			}
			if (map->locations[free_width_i] != NULL)
				free(map->locations[free_width_i]);
		}
		return 0;
	}
	
	
	return map;
	
}
	


/*
 * frees memory used by map
 */
int mW_map_destroy(mW_map * map)
{
	
	mW_map_location * next_location;
	mW_map_location * last_location;
	
	unsigned short width_i, height_i;
	
	for (width_i = 0; width_i < map->width; width_i++)
	{
		for (height_i = 0; height_i < map->height; height_i++)
		{
			
			next_location = map->locations[width_i][height_i]->next_location;
			
			while (map->locations[width_i][height_i]->next_location != NULL)
			{
				
				last_location = map->locations[width_i][height_i];
				next_location = map->locations[width_i][height_i]->next_location;
				
				while (next_location->next_location != NULL)
				{
					last_location = next_location;
					next_location = next_location->next_location;
				}
				
				free(next_location);
				last_location->next_location = NULL;
				
			}
			
			free(map->locations[width_i][height_i]);
		}
		free(map->locations[width_i]);
	}
	free(map->locations);
	
	free(map);
	
	return 1;
	
}



unsigned int mW_map_get_x_pixel (mW_map * map, unsigned short x)
{
	
	return ((x - map->left) * mW_TILE_SIZE) + map->offset_x;
	
}



unsigned int mW_map_get_y_pixel (mW_map * map, unsigned short y)
{
	
	return ((y - map->top) * mW_TILE_SIZE) + map->offset_y;
	
}



int mW_map_get_x_tile (mW_map * map, int pixel_x)
{
	
	int tile = ((pixel_x + map->offset_x) / mW_TILE_SIZE) + map->left;
	
	if ((tile < 0) || (tile >= map->width))
		return -1;
	else
		return tile;

}



int mW_map_get_y_tile (mW_map * map, int pixel_y)
{
	
	int tile = ((pixel_y + map->offset_y) / mW_TILE_SIZE) + map->top;
	
	if ((tile < 0) || (tile >= map->width))
		return -1;
	else
		return tile;

}



/*
 * loads and initializes a map from a map filec structs
 *   returns a pointer to the mW_map on success, NULL on error
 */
mW_map * mW_map_load_file (const char * filename)
{
	
	int read_int = 0;
	unsigned short map_width;
	unsigned short map_height;
	
	unsigned short width_i;
	unsigned short height_i;
	
	mW_map * map;
	
	FILE * map_file;
	
	
	// open file for reading
	map_file = fopen(filename, "r");
	if (map_file == NULL)
		mW_error("Could not open map file");



	// read map width and height
	if (fread(&read_int, 2, 1, map_file) < 1)
		mW_error("Error reading map");
	map_width = (unsigned short) read_int;
		
	if (fread(&read_int, 2, 1, map_file) < 1)
		mW_error("Error reading map");
	map_height = (unsigned short) read_int;
	

	map = mW_map_create(map_width, map_height);
	if (map == NULL)
		mW_error("Error creating map");
	
	
	// read in locations
	for (height_i = 0; height_i < map->height; height_i++)
	{
		
		for (width_i = 0; width_i < map->width; width_i++)
		{
			
			// tile_id
			if (fread(&read_int, 2, 1, map_file) < 1)
			{
				mW_map_destroy(map);
				mW_error("Error reading map tile");
			}
			
			map->locations[width_i][height_i]->tile_id = 0;
			map->locations[width_i][height_i]->tile_id = (unsigned short) read_int;
			
			
			// flags
			if (fread(&read_int, 1, 1, map_file) < 1)
			{
				mW_map_destroy(map);
				mW_error("Error reading map flags");
			}
			
			map->locations[width_i][height_i]->flags = (unsigned char) read_int;
			
		}
	}
	
	fclose(map_file);
	
	return map;
	
}



/*
 * Returns 1 if the mW_map_location is passable
 */
int mW_map_location_passable (const mW_map_location * location)
{
	
	if (location->flags & 1)
		return 1;
	else
		return 0;
		
}



int mW_map_passable(mW_map * map, unsigned short x, unsigned short y)
{
	
	if ((x >= map->width) || (y >= map->height))
		return 0;
	
	if (map->locations[x][y]->flags & 1)
		return 1;
	else
		return 0;
		
}



int mW_map_scroll(mW_map * map, int x, int y)
{
	
	map->left += x;
	map->top += y;
	
	map->offset_x += x * mW_TILE_SIZE;
	map->offset_y += y * mW_TILE_SIZE;
	
	return 1;
	
}



int mW_map_scroll_to (mW_map * map, int x, int y)
{
		
	mW_map_scroll(map, x - map->left, y - map->top);
	
	return 1;
	
}



int mW_map_set_display_dimensions (mW_map * map, int display_width, int display_height)
{

	if ((display_width < 0) || (display_height < 0))
		return 0;
	
	map->display_width = display_width;
	map->display_height = display_height;

	return 1;
	
}



int mW_map_update_offsets (mW_map * map)
{
	
	if (map->offset_x > 0)
	{
		if (map->offset_x < mW_MAP_SCROLL_SPEED)
			map->offset_x = 0;
		else
			map->offset_x -= mW_MAP_SCROLL_SPEED;
	}
	else if (map->offset_x < 0)
	{
		if (map->offset_x * -1 < mW_MAP_SCROLL_SPEED)
			map->offset_x = 0;
		else
			map->offset_x += mW_MAP_SCROLL_SPEED;
	}
	
	if (map->offset_y > 0)
	{
		if (map->offset_y < mW_MAP_SCROLL_SPEED)
			map->offset_y = 0;
		else
			map->offset_y -= mW_MAP_SCROLL_SPEED;
	}
	else if (map->offset_y < 0)
	{
		if (map->offset_y * -1 < mW_MAP_SCROLL_SPEED)
			map->offset_y = 0;
		else
			map->offset_y += mW_MAP_SCROLL_SPEED;
	}
	
	return 1;
	
}
